package core

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"
	"tone-agent/entity"

	"github.com/spf13/viper"
)

func ScanResult() []string {
	files, _ := ioutil.ReadDir(entity.ResultPath)
	var filesList []string

	for _, f := range files {
		filename := f.Name()
		if strings.HasSuffix(filename, ".json") {
			filesList = append(filesList, filename)
		}
	}
	return filesList
}

func ReadResult(tid string) (map[string]string, bool) {
	file := fmt.Sprintf("%s/%s.json", entity.ResultPath, tid)
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return nil, false
	}
	result, _ := ioutil.ReadFile(file)
	resultMap := make(map[string]string)
	err2 := json.Unmarshal(result, &resultMap)
	if err2 != nil {
		MyLogger.WithFields(LogFields{
			"tid":    tid,
			"file":   file,
			"result": result,
			"error":  err2.Error(),
		}).Error("Read result file unmarshal failed!")
		return nil, false
	}
	return resultMap, true
}

func SaveResult(resultData map[string]string) {
	file := fmt.Sprintf("%s/%s.json", entity.ResultPath, resultData["tid"])
	content, _ := json.Marshal(resultData)
	if err := WriteFile(file, content, 0666); err != nil {
		MyLogger.WithFields(LogFields{
			"file":    file,
			"content": content,
			"error":   err.Error(),
		}).Error("Save result failed")
	} else {
		MyLogger.WithFields(LogFields{
			"file":    file,
			"content": string(content),
		}).Info("Save result success")
	}
}

func UpdateResult(updateData map[string]string) error {
	resultData, suc := ReadResult(updateData["tid"])
	if !suc {
		MyLogger.WithFields(LogFields{
			"tid": updateData["tid"],
		}).Info("Update result file failed!")
		return nil
	}
	for key, value := range updateData {
		resultData[key] = value
	}
	SaveResult(resultData)
	ud, _ := json.Marshal(updateData)
	MyLogger.WithFields(LogFields{
		"tid":           updateData["tid"],
		"updateContent": string(ud),
	}).Info("Update result file success!")
	return nil
}

func RemoveResult(tid string) error {
	file := fmt.Sprintf("%s/%s.json", entity.ResultPath, tid)
	err := os.Remove(file)
	MyLogger.WithFields(LogFields{
		"tid": tid,
	}).Info("Remove result file success!")
	return err
}

func SyncResult(resultData map[string]string) bool {
	resultData["sign"] = GetSign()
	resultData["tsn"] = viper.GetString(entity.TSNConfigKey)
	tid := resultData["tid"]
	url := fmt.Sprintf("%s/%s", viper.Get(entity.ProxyConfigKey), entity.SyncResultAPI)
	jsonValue, _ := json.Marshal(resultData)
	client := GetHttpClient()
	resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonValue))
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		MyLogger.WithFields(LogFields{
			"error": err.Error(),
			"tid":   tid,
		}).Error("Sync result to toneagent-proxy error")
		return false
	}
	if resp.StatusCode != 200 {
		MyLogger.WithFields(LogFields{
			"error": resp.Body,
			"tid":   tid,
		}).Error("Sync result to toneagent-proxy error")
		return false
	}
	var res map[string]interface{}
	body, _ := ioutil.ReadAll(resp.Body)

	err2 := json.Unmarshal(body, &res)
	if err2 != nil {
		MyLogger.WithFields(LogFields{
			"error": err2.Error(),
			"tid":   tid,
			"body":  body,
		}).Error("Sync result to toneagent-proxy error")
		return false
	}
	if res["SUCCESS"] == entity.ResponseOk {
		rd, _ := json.Marshal(resultData)
		if resultData["status"] == entity.TaskCompletedStatus {
			MyLogger.WithFields(LogFields{
				"tid":    tid,
				"result": string(rd),
			}).Info("Sync result to toneagent-proxy success")
			RemoveResult(tid)
		} else {
			MyLogger.WithFields(LogFields{
				"tid":    tid,
				"result": string(rd),
			}).Info("Sync result to toneagent-proxy success")
		}
		return true
	}
	return false
}

func CheckTaskProcess() {
	files, _ := ioutil.ReadDir(entity.ResultPath)
	for _, f := range files {
		filename := f.Name()
		tid := strings.TrimSuffix(filename, ".json")
		resultMap, _ := ReadResult(tid)
		if resultMap["status"] == entity.TaskRunningStatus {
			taskPid, _ := strconv.Atoi(resultMap["task_pid"])
			pidExist := CheckPid(taskPid)
			if !pidExist {
				resultMap["status"] = entity.TaskCompletedStatus
				resultMap["error_code"] = entity.PidNotExistErrorCode
				resultMap["error_msg"] = entity.PidNotExistErrorMsg
				MyLogger.WithFields(LogFields{
					"tid": resultMap["tid"],
					"pid": taskPid,
				}).Info("Process does not exist, set task status to completed")
				UpdateResult(resultMap)
			}
		}
	}
}

func RecoverTaskProcess() {
	files, _ := ioutil.ReadDir(entity.ResultPath)
	for _, f := range files {
		filename := f.Name()
		tid := strings.TrimSuffix(filename, ".json")
		resultMap, _ := ReadResult(tid)
		if resultMap["status"] == entity.TaskRunningStatus {
			taskPid, _ := strconv.Atoi(resultMap["task_pid"])
			pidExist := CheckPid(taskPid)
			if pidExist {
				go monitorProcess(tid, taskPid)
			}
		}
	}
}

func monitorProcess(tid string, pid int) {
	var (
		errorCode string
		errorMsg  string
		exitCode  string
	)
	identFile := fmt.Sprintf("%s/%s", entity.ScriptPath, tid)
	stdoutFile := fmt.Sprintf("%s/%s.stdout", entity.TaskOutputPath, tid)
	stderrFile := fmt.Sprintf("%s/%s.error", entity.TaskOutputPath, tid)

	// 监控进程状态
	ticker := time.NewTicker(5 * time.Second) // 每5秒检查一次进程状态
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			pidExist := CheckPid(pid)
			if !pidExist {
				stdout, _ := ioutil.ReadFile(stdoutFile)
				result := string(stdout)

				stderr, _ := ioutil.ReadFile(stderrFile)
				if len(stderr) > 0 {
					errorMsg = fmt.Sprintf("%s(%s)", entity.ExecCmdErrorMsg, string(stderr))
					errorCode = entity.ExecCmdErrorCode
				}

				var resultData = map[string]string{}
				resultData = map[string]string{
					"status":      entity.TaskCompletedStatus,
					"tid":         tid,
					"task_pid":    strconv.Itoa(pid),
					"error_code":  errorCode,
					"error_msg":   errorMsg,
					"result":      result,
					"exit_code":   exitCode,
					"finish_time": GetCurTimeStr(),
				}
				MyLogger.WithFields(LogFields{
					"tid":     tid,
					"env":     nil,
					"args":    nil,
					"timeout": nil,
					"ident":   identFile,
					"result":  result,
				}).Info("Task execution successfully, the main process is lost and the tid process run end.")
				UpdateResult(resultData)
				os.Remove(identFile)
				os.Remove(stdoutFile)
				os.Remove(stderrFile)
				return
			}
			// case <-time.After(60 * 60 * 24 * 10 * time.Second):
		}
	}
}
